﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace _6._1_MyMathTool
{
    public class MathTool
    {
        #region 1.二维点A,二维点B, 长度L(L>0 表示AB延长L;L<0 表示AB收缩L),计算该点坐标
        //double[] pointA; A点的坐标
        //double[] pointB; B点的坐标
        //double L; L的长度
        public static double[] LPointCoordinates(double[] pointA, double[] pointB, double L)
        {
            // 向量AB的坐标
            double vectorAB_X = pointB[0] - pointA[0];
            double vectorAB_Y = pointB[1] - pointA[1];
            // 向量AB的长度
            double vectorAB_length = Math.Sqrt(Math.Pow(vectorAB_X, 2) + Math.Pow(vectorAB_Y, 2));

            //AB的单位向量
            double unit_vector_AB_X = vectorAB_X / vectorAB_length;
            double unit_vector_AB_Y = vectorAB_Y / vectorAB_length;

            // L的长度
            double L_Length = Math.Abs(L);

            // 如果L小于0，说明向量AB需要收缩，所以要把向量AL变成负值
            if (L < 0)
            {
                L_Length = -L_Length;
            }
            // 向量AL的X,Y坐标
            double vectorAL_X = unit_vector_AB_X * L_Length;
            double vectorAL_Y = unit_vector_AB_Y * L_Length;

            // L的坐标
            double L_X = pointA[0] + vectorAB_X + vectorAL_X;
            double L_Y = pointA[1] + vectorAB_Y + vectorAL_Y;
            return new double[] { L_X, L_Y };

        }
        #endregion

        #region 2.二维A,通过点A的方向角,通过A点平移值offset(左侧为负值,右侧为正值),计算该点坐标
        //public static double[] AOffsetCoordinate(double[] pointA, double offset)
        //{
        //    // double[] pointA; A点坐标
        //    // offset 平移的距离
        //    // double[] A_Offset 平移后的A点

        //    // A点X轴的平移值
        //    //double A_Xoffset = pointA[0] + offset;
        //    //double[] A_Offset = new double[2] { A_Xoffset, pointA[1] };
        //    //return A_Offset;



        //}

        #endregion

        #region 3.二维点A,二维点B,旋转角Rotation_A(绕A点,逆时针旋转),通过A点的极坐标半径长度为R,求该点坐标

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pointA">点A的坐标</param>
        /// <param name="pointB">点B的坐标</param>
        /// <param name="r">旋转的角度</param>
        /// <param name="rotation_A">半径</param>
        /// <returns> B点的坐标 </returns>
        public static double[] BRotationCoordinates(double[] pointA, double[] pointB, double r, double rotation_A)
        {
            double vectorABx = pointB[0] - pointA[0];
            double vectorABy = pointB[1] - pointA[1];
            // 向量AB的模
            double vectorAB_length = Math.Sqrt(Math.Pow(vectorABx, 2) + Math.Pow(vectorABy, 2));
            double B_y = Math.Round(Math.Sin(rotation_A * Math.PI / 180), 2) * vectorAB_length + pointA[1];
            double B_x = Math.Round(Math.Cos(rotation_A * Math.PI / 180), 2) * vectorAB_length + pointA[0];

            // 旋转后B的坐标
            pointB = new double[] { B_x, B_y };
            // 判断vectorAB_length与r的值
            if (r < 0)
            {
                throw new Exception("r不能为负数");
            }
            else if (vectorAB_length == r)
            {
                return pointB;
            }
            else if (vectorAB_length < r)
            {
                r = r - vectorAB_length;
                // 调第一题的类
                return LPointCoordinates(pointA, pointB, r);
            }
            else{
                r = vectorAB_length - r  ;
                r = - r;
                return LPointCoordinates(pointA, pointB, r);
            }
           

        }
        #endregion

        #region 4.三维点A,三维点B,三维点C,求通过A点的三角形ABC的单位法向量(有两个, 其中一个符合右手定则,要这个)
        public static double[] NormalVector(double[] pointA, double[] pointB, double[] pointC)
        {
            // 向量AB
            double AB_Vector_x = pointB[0] - pointA[0];
            double AB_Vector_y = pointB[1] - pointA[1];
            double AB_Vector_z = pointB[2] - pointA[2];
            // 向量AC
            double AC_Vector_x = pointC[0] - pointA[0];
            double AC_Vector_y = pointC[1] - pointA[1];
            double AC_Vector_z = pointC[2] - pointA[2];

            double normalVector_x = AB_Vector_y * AC_Vector_z - AC_Vector_y * AB_Vector_z;
            double normalVector_y = AC_Vector_x * AB_Vector_z - AC_Vector_z * AB_Vector_x;
            double normalVector_z = AB_Vector_x * AC_Vector_y - AC_Vector_x * AB_Vector_y;
            return new double[] { normalVector_x, normalVector_y, normalVector_z };

        }
        #endregion

        #region 5.二维点A,B,C,D,求AB与CD交点
        /// <summary>
        /// 
        /// </summary>
        /// <param name="A">A的坐标</param>
        /// <param name="B">B的坐标</param>
        /// <param name="C">C的坐标</param>
        /// <param name="D">D的坐标</param>
        /// <returns></returns>
        public static double[] Intersection(double[] pointA, double[] pointB, double[] pointC, double[] pointD)
        {
            double x; // 交点的x坐标
            double y; // 交点的y坐标
            double AB_k; // AB的斜率
            double CD_k; // CD的斜率

            
            if (pointB[0] == pointA[0] && pointB[1] == pointA[1] || pointD[0] == pointC[0] && pointD[1] == pointC[1])
            {
                throw new Exception("点位重合了");
            }
            // AB与CD都垂直于x轴
            else if (pointB[0] - pointA[0] == 0 && pointD[0] - pointC[0] == 0)
            {
                throw new Exception("AB与CD平行,没有交点");
            }
            // AB垂直于x轴
            else if (pointB[0] - pointA[0] == 0)
            {
                x = pointA[0];
                CD_k = (pointD[1] - pointC[1]) / (pointD[0] - pointC[0]);
                y = CD_k * (x - pointC[0]) + pointC[1];

            }
            // CD 垂直于x轴
            else if (pointD[0] - pointC[0] == 0)
            {
                x = pointC[0];
                AB_k = (pointB[1] - pointA[1]) / (pointB[0] - pointA[0]);
                y = AB_k * (x - pointA[0]) + pointA[1];
            }
            else
            {

                AB_k = (pointB[1] - pointA[1]) / (pointB[0] - pointA[0]);
                CD_k = (pointD[1] - pointC[1]) / (pointD[0] - pointC[0]);

                if (AB_k == CD_k)
                {
                    throw new Exception("AB与CD平行");
                }
                // 联立两个直线方程
                // AB: y = k1(x - x1) + y1
                // CD: y = k2(x - x2) + y2
                x = (AB_k * pointA[0] - pointA[1] - CD_k * pointC[0] + pointC[1]) / (AB_k - CD_k);
                // y = kx + b
                y = AB_k * (x - pointA[0]) + pointA[1];
            }
            return new double[] { x, y };
        }
        #endregion

        #region 6.二维点O,半径R, 圆外一点A,求切点
        public static double[][] pointOfTangency(double[] pointO, double[] pointA, double R)
        {
            // AO向量的x
            double AO_vector_x = pointO[0] - pointA[0];
            // AO向量的y
            double AO_vector_y = pointO[1] - pointA[1];
            // AO向量的模
            double AO_length = Math.Sqrt(Math.Pow(AO_vector_x, 2) + Math.Pow(AO_vector_y, 2));
            if (AO_length < R)
            {
                throw new Exception("没有切点");
            }
            else if (AO_length == R)
            {
                // 切点的坐标为A点的坐标
                return new double[][] { new double[] { pointA[0], pointA[1] } };

            }
            else
            {
                // A到切点的距离
                double A_pointOfTangency = Math.Sqrt(Math.Pow(AO_length, 2) - Math.Pow(R, 2));
                // 向量AO的单位向量
                double unit_vector_AO_X = AO_vector_x / AO_length;
                double unit_vector_AO_Y = AO_vector_y / AO_length;
                // AO的角度
                double AO_angle = Math.Atan2(unit_vector_AO_Y, unit_vector_AO_X);
                // AO 与切点的角度
                double OA_pointOfTangency_angle = Math.Asin(R / AO_length);
                // 切点与x轴的角度
                double pointOfTangency_angle = AO_angle - OA_pointOfTangency_angle;
                double pointOfTangency_angle1 = AO_angle + OA_pointOfTangency_angle;
                // A切点的单位向量
                double unit_vector_A_pointOfTangency_x = Math.Cos(pointOfTangency_angle);
                double unit_vector_A_pointOfTangency_y = Math.Sin(pointOfTangency_angle);
                // A切点的单位向量
                double unit_vector_A_pointOfTangency_X = Math.Cos(pointOfTangency_angle1);
                double unit_vector_A_pointOfTangency_Y = Math.Sin(pointOfTangency_angle1);

                double x = Math.Round(unit_vector_A_pointOfTangency_x * A_pointOfTangency + pointA[0], 2);
                double y = Math.Round(unit_vector_A_pointOfTangency_y * A_pointOfTangency + pointA[1], 2);

                double X = Math.Round(unit_vector_A_pointOfTangency_X * A_pointOfTangency + pointA[0], 2);
                double Y = Math.Round(unit_vector_A_pointOfTangency_Y * A_pointOfTangency + pointA[1], 2);

                return new double[][] { new double[] { x, y }, new double[] { X, Y } };
            }
        }
        #endregion

        #region 7.二维点O,半径R,构成圆, 二维点A,B,求AB与圆的交点
        public static double[][] pointABIntersection(double[] pointO, double[] pointA, double[] pointB, double R)
        {
            // 直线方程的ABC
            double A = pointA[1] - pointB[1];
            double B = pointB[0] - pointA[0];
            double C = pointA[0] * pointB[1] - pointB[0] * pointA[1];
            // 圆-方程的abc
            double a = Math.Pow(B, 2) + Math.Pow(A, 2);
            double b = 2 * B * C + 2 * pointO[0] * A * B - Math.Pow(A, 2) * 2 * pointO[1];
            double c = Math.Pow(C, 2) + A * 2 * pointO[0] * C + Math.Pow(A, 2) * Math.Pow(pointO[0], 2) + Math.Pow(A, 2) * Math.Pow(pointO[1], 2) - Math.Pow(A, 2) * Math.Pow(R, 2);

            double y;
            double x;

            if (A == 0)
            {
                y = Math.Round(-C / B, 2);
                double deita = Math.Sqrt(Math.Pow(R, 2) - Math.Pow(pointO[0], 2) - Math.Pow(y, 2) + 2 * pointO[1] * y - Math.Pow(pointO[1], 2) + Math.Pow(pointO[0], 2));
                if (deita > 0)
                {
                    x = Math.Round(deita + pointO[0], 2);
                    double x1 = Math.Round(pointO[0] - deita, 2);
                    return new double[][] { new double[] { x, y }, new double[] { x1, y } };

                }
                else if (deita == 0)
                {
                    x = Math.Round(deita + pointO[0], 2);
                    return new double[][] { new double[] { x, y } };
                }
                else
                {
                    throw new Exception("无解");
                }
            }
            else if (Math.Pow(b, 2) - 4 * a * c < 0)
            {
                throw new Exception("无解");
            }
            else if (Math.Pow(b, 2) - 4 * a * c == 0)
            {
                y = Math.Round((-b + Math.Sqrt(Math.Pow(b, 2) - 4 * a * c)) / (2 * a), 2);
                x = Math.Round(-(C + B * y) / A, 2);
                return new double[][] { new double[] { x, y } };
            }
            else
            {
                y = Math.Round((-b + Math.Sqrt(Math.Pow(b, 2) - 4 * a * c)) / (2 * a), 2);
                double Y = Math.Round((-b - Math.Sqrt(Math.Pow(b, 2) - 4 * a * c)) / (2 * a), 2);
                x = Math.Round(-(C + B * y) / A, 2);
                double X = Math.Round(-(C + B * Y) / A, 2);
                return new double[][] { new double[] { x, y }, new double[] { X, Y } };
            }
        }
        #endregion

        #region 8.二维点O,半径R,求圆上任一点的切线方向角
        public static double tangentDirectionAngle(double[] pointO, double R, double[] pointA)
        {
            // OA向量
            double AO_vector_x = pointA[0] - pointO[0];
            double AO_vector_y = pointA[1] - pointO[1];
            // OA的模
            double AO_vector_length = Math.Sqrt(Math.Pow(AO_vector_x, 2) + Math.Pow(AO_vector_y, 2));
            if (AO_vector_length > R || AO_vector_length < R)
            {
                throw new Exception("点不在圆上");
            }
            else
            {
                double x = (Math.Pow(pointA[0], 2) - pointA[0] * pointO[0] + Math.Pow(pointA[1], 2) - pointA[1] * pointO[1]) / (pointA[0] - pointO[0]);
                if (pointA[0] == x)
                {
                    return 90;
                }
                else if (pointA[1] == 0)
                {
                    return 180;
                }
                else
                {
                    //  QA向量
                    double QA_vector_x = pointA[0] - x;
                    double QA_vector_y = pointA[1] - 0;
                    double QA_length = Math.Sqrt(Math.Pow(QA_vector_x, 2) + Math.Pow(QA_vector_y, 2));
                    double unit_vector_x = QA_vector_x / QA_length;
                    double unit_vector_y = QA_vector_y / QA_length;
                    double angle = Math.Atan2(unit_vector_y, unit_vector_x) * 180 / Math .PI;
                    if (pointA[0] < 0 && pointA[1] > 0)
                    {
                        angle = 180 - angle;
                    }
                    return angle;
                }



            }
        }


        #endregion
    }
}
